#include <pcap.h>
#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <stdbool.h>

#include <errno.h>
#include <unistd.h>
#define once_number 64

struct CAN_layer{
  u_int16_t pkg_type;
  u_int16_t layer_addr_type;
  u_int16_t layer_addr_len;
  u_char unused[8];  //注意字节对齐
  u_int16_t protocol;
};

struct CAN_content{
  u_int32_t canid;
  u_char dlc;
  u_char reserved[3];
  u_char data[8];
};

struct CANID_unit{
  u_int32_t canid[64];
};

pcap_t* getOpenDev(char* errBuf);
void ips_send(u_char *args, const struct pcap_pkthdr *header, const u_char *packet);

int number = 0;
bool theFirst = 1;
struct timeval ts;
int now_order = 0;
struct CANID_unit *canid_unit;

void ERR(int errCode, char* errMsg)
{
  switch(errCode)
  {
    case 1:
      printf("[001] Error: %s\n", errMsg);
      exit(-1);
    case 2:
      printf("[002] Wrong order. Ctrl+C to quit or Input again: ");
      break;
    case 30:
      printf("[030] malloc error.\n");
      exit(-1);
    case 33:
      printf("[033] fork error.\n");
      break;
    default:
      printf("other error.\n");
  }
}

int main()
{
  pcap_t* dev;
  char* filter_exp = "";  //TODO
  char errBuf[PCAP_ERRBUF_SIZE];

  struct bpf_program fp;
  bpf_u_int32 net = 0;

  // Step 1: 打开pcap会话
  dev = getOpenDev(errBuf);

  // Step 2: 为侦听进程(bpf_program)fp添加过滤条件
  pcap_compile(dev, &fp, filter_exp, 0, net);
  pcap_setfilter(dev, &fp);

  canid_unit = (struct CANID_unit*)malloc(sizeof(struct CANID_unit));
  if(!canid_unit)
    ERR(30, NULL);

  // Step 4: 抓取报文，回调函数ips_send，功能是每抓取64条报文就按格式发往检测系统
  pcap_loop(dev, -1, ips_send, NULL);

  pcap_close(dev); //关闭设备句柄

  return 0;
}

pcap_t* getOpenDev(char* errBuf)
{
  pcap_if_t *devAll, *pdev;
  int index;

  //look for all the net device and print them
  pcap_findalldevs(&devAll, errBuf);
  if(devAll){
    index=0;
    printf("Please choose the dev by the order: \n");
    for(pdev = devAll; pdev; pdev=pdev->next)
    {
      index++;
      printf("%d. %s\n", index, pdev->name);
    }
  }
  else ERR(1, errBuf);

  //choose one dev to open
  int option=0;
  scanf("%d", &option);
  while(option<1||option>index){
    ERR(2, NULL);
    scanf("%d", &option);
  }
  for(pdev = devAll;option>1; pdev=pdev->next, option--);
  printf("Try to open device %s...\n", pdev->name);
  pcap_t* dev = pcap_open_live(pdev->name, BUFSIZ, 0, 1000, errBuf);
  if(!dev) ERR(1, errBuf);

  return dev;
}

//for debug
void printPkg(struct pcap_pkthdr *header, struct CAN_layer *layer, struct CAN_content *content)
{
  char buf[1024], dataArea[10];
  //将数据按格式写入buf中
  sprintf(buf, "Timestamp:       %4ld.%06ld        ID: %04x    000    DLC: %x    ",
           header->ts.tv_sec, header->ts.tv_usec, content->canid, content->dlc);
  for(int i=0;i<content->dlc;i++)
  {
    sprintf(dataArea, "%02x", content->data[i]);
    strcat(buf, dataArea);
    if(i+1<content->dlc)
      strcat(buf, " ");
    else
      strcat(buf, "\n");
  }
  printf("%s",buf);
}

//for debug
void diffstamp(struct timeval* stamp1, struct timeval stamp2)
{
  stamp1->tv_sec = stamp1->tv_sec - stamp2.tv_sec;
  stamp1->tv_usec = stamp1->tv_usec - stamp2.tv_usec;
  if(stamp1->tv_usec<0)
  {
    stamp1->tv_sec -=1;
    stamp1->tv_usec += 1000000;
  }
}

void ips_send(u_char *args, const struct pcap_pkthdr *header, const u_char *packet)
{
  //初始化时间戳
  //if(theFirst){ts=(struct timeval)header->ts;theFirst = 0;};

  //3.进行内存操作
  //char dataArea[10];
  if(number<once_number)
  {
    struct CAN_layer *layer=(struct CAN_layer *)packet;
    struct CAN_content *content=(struct CAN_content *)(packet+sizeof(struct CAN_layer));
    //TODO: 是否需要只过滤CAN报文
    if(ntohs(layer->protocol)!=0x0c)
      return ;

    //diffstamp((struct timeval*)&header->ts, ts);
    //printPkg((struct pcap_pkthdr *)header, layer, content);

    canid_unit->canid[number]=content->canid;
    number++;
    if(number==once_number)
    {
      now_order=(now_order+1)%1000;
      number = 0;
      pid_t fpid = fork();
      if(fpid<0)
        ERR(33, NULL);
      else if(fpid==0)
      {
        char temp[10],argv[500]="./IDS ";
        int i;
        for(i=0;i<once_number;i++){
          sprintf(temp,"%04x ", canid_unit->canid[i]);
          strcat(argv, temp);
        }
        sprintf(temp,"%d", now_order);
        strcat(argv, temp);
        system(argv);
        //printf("%s\n", argv);
        exit(0);
      }
      else{
        free(canid_unit);
        canid_unit = (struct CANID_unit*)malloc(sizeof(struct CANID_unit));
        if(!canid_unit)
          ERR(30, NULL);
      }
    }
  }
  else{
    ERR(1, "What happened?");
  }
}
